#include <iostream>

#include <cstdlib>

#include "EzImage.hpp"

/*

    3 6 9

  0 1 2 3

  4 5 6 7

  8 9 a b

*/

/*

  rgbargbargbargba

  rgbargbargbargba

  rgbargbargbargba



*/





using std::cout;

using std::endl;





EzImage::EzImage(const char *nom){

  setSpeaking(true);

  if(speaking) cout << "EzImage constructor" <<endl;





  ilGenImages(1, &(this->image)); // 1er parametre est le nombre d images a generer

  ilBindImage(this->image); // image = image en cours

  if(ilLoadImage(nom) == IL_FALSE)

    cout << "Loading Failure @"<< nom << endl;



  ilConvertImage(IL_RGBA, IL_UNSIGNED_BYTE);

  ILubyte *aux = ilGetData();

  width = ilGetInteger(IL_IMAGE_WIDTH);

  height = ilGetInteger(IL_IMAGE_HEIGHT);

  data = new ILubyte[width * height * 4]; // pour appliquer plusieurs filtres

  for(unsigned i = 0; i < (width * height * 4); ++i)

    data[i] = aux[i];





  ilSetData(data);



}



EzImage::~EzImage(){

  if(speaking) cout <<"EzImage destructor"<<endl;

  if(data){

    delete[] data;

    data = NULL;

  }

  ilDeleteImages(1, &(this->image)); // 1er param est le nombre d images a supprimer

}





ILboolean EzImage::sauvegarder_image(const char *nom){

  if(speaking) cout <<"EzImage save"<<endl;

  ilEnable(IL_FILE_OVERWRITE); // sinon n ecrase pas

  return ilSaveImage(nom);

}



void EzImage::setSpeaking(bool b)

{

  speaking=b;

}





//on prend tous les pixels et on leur affecte la valeur: somme des pixels dans un rayon de radius / nombre de ces pixels

void EzImage::flou(unsigned radius){

  if(radius == 0) // useless ds ce cas

    return;

  

  struct get{

    ILubyte *data2;

    unsigned radius;

    ILuint widthDebut;

    ILuint widthFin;

    ILuint heightDebut;

    ILuint heightFin;

  }arg;

  arg.data2 = new ILubyte[(this->width * this->height) * 4];

  arg.radius = radius;

  arg.heightDebut = 0;

  arg.heightFin = height;

  arg.widthDebut = 0;

  arg.widthFin = width * 4;

  flou_etape((void*)&arg); // changer ces valeurs et attacher la fonction aux threads



  ilSetData(arg.data2);



}





void* EzImage::flou_etape(void *arg){

  int total;

  struct get{

    ILubyte *data2;

    unsigned radius;

    ILuint widthDebut;

    ILuint widthFin;

    ILuint heightDebut;

    ILuint heightFin;

  };



  ILubyte *data2 = ((get*)arg)->data2;

  unsigned radius = ((get*)arg)->radius;

  ILuint widthDebut = ((get*)arg)->widthDebut;

  ILuint widthFin = ((get*)arg)->widthFin;

  ILuint heightDebut = ((get*)arg)->heightDebut;

  ILuint heightFin = ((get*)arg)->heightFin;

	

  for(int y = heightDebut; y < heightFin; ++y){

    for(int x = widthDebut; x < widthFin; ++x){ // on multiplie par 4 car un pixel est represente par 4 valeurs(rgba)

      unsigned cpt = 1;

      total = data[indice(x, y)];

      for(int i = 1; i <= radius; ++i){

	if((y - i) >= 0){

	  total += data[indice(x, y - i)];

	  ++cpt;

	}



	if((y + i) < this->height){

	  total += data[indice(x, y + i)];

	  ++cpt;

	}



	if((x - (i * 4)) >= 0 && (y - i) >= 0){

	  total += data[indice(x - (i * 4), y - i)];

	  ++cpt;

	}



	if((x - (i * 4)) >= 0){

	  total += data[indice(x - (i * 4), y)];

	  ++cpt;

	}



	if((x - (i * 4)) >= 0 && (y + i) < this->height){

	  total += data[indice(x - (i * 4), y + i)];

	  ++cpt;

	}



	if((x + (i * 4)) < ((this->width) * 4) && (y - i) >= 0){

	  total += data[indice(x + (i * 4), y - i)];

	  ++cpt;

	}



	if((x + (i * 4)) < ((this->width) * 4)){

	  total += data[indice(x + (i * 4), y)];

	  ++cpt;

	}



	if((x + (i * 4)) < ((this->width) * 4) && (y + i) < this->height){

	  total += data[indice(x + (i * 4), y + i)];

	  ++cpt;

	}



      }



      total /= cpt;

      data2[indice(x, y)] = total;



    }

  }

  return NULL;

}





void EzImage::negative(){

  struct get{

    ILubyte *data2;

    ILuint debut;

    ILuint fin;

  }arg;

  arg.debut = 0;

  arg.fin = width * height * 4;

  arg.data2 = new ILubyte[(this->width * this->height) * 4];

  negative_etape((void*)&arg); // changer valeurs pour threads

  ilSetData(arg.data2);

}



void* EzImage::negative_etape(void *arg){

  struct get{

    ILubyte *data2;

    ILuint debut;

    ILuint fin;

  };



  ILubyte *data2 = ((get*)arg)->data2;

  ILuint debut = ((get*)arg)->debut;

  ILuint fin = ((get*)arg)->fin;

  for(unsigned i = debut; i < fin; ++i)

    data2[i] = 255 - data[i];

  return NULL;

}



// wikipedia pour l'algo(j ai pris le plus simple ici)

void EzImage::edge_detect(){

  struct get{

    ILubyte *data2;

    ILuint widthDebut;

    ILuint widthFin;

    ILuint heightDebut;

    ILuint heightFin;

  }arg;

  arg.heightDebut = 0;

  arg.heightFin = height;

  arg.widthDebut = 0;

  arg.widthFin = width * 4;

	

  arg.data2 = new ILubyte[(this->width * this->height) * 4];

  edge_detect_etape((void*)&arg); // changer valeurs pour threads

  ilSetData(arg.data2);

}



void* EzImage::edge_detect_etape(void *arg){

  float aux1, aux2, res;



  struct get{

    ILubyte *data2;

    ILuint widthDebut;

    ILuint widthFin;

    ILuint heightDebut;

    ILuint heightFin;

  };



  ILubyte *data2 = ((get*)arg)->data2;

  ILuint widthDebut = ((get*)arg)->widthDebut;

  ILuint widthFin = ((get*)arg)->widthFin;

  ILuint heightDebut = ((get*)arg)->heightDebut;

  ILuint heightFin = ((get*)arg)->heightFin;



  for(int y = heightDebut; y < heightFin; ++y){

    for(int x = widthDebut; x < widthFin; ++x){

      if((x + 4) < (width * 4) && (x - 4) >= 0 && (y - 1) >= 0 && (y + 1) < height){



	aux1 = data[indice(x + 4, y)] - data[indice(x - 4, y)];

	aux1 *= aux1;



	aux2 = data[indice(x, y - 1)] - data[indice(x, y + 1)];

	aux2 *= aux2;

	res = sqrt(aux1 + aux2);



	if(res > 255){

	  res = 255;



	}

	data2[indice(x, y)] = (unsigned)res;



      }

      else // bordure

	data2[indice(x, y)] = 255;

    }

  }



  ilSetData(data2);

  return NULL;

}
